using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using Decav.Gatsb.Application.Configuration;
using System.Configuration;
using Decav.Gatsb.Mobile;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using Decav.Gatsb.Configuration;
using System.Workflow.Activities;
using Decav.Gatsb.Data;
using System.Diagnostics;
using System.Workflow.ComponentModel;
using Decav.Diagnostics;
using Decav.Gatsb.Diagnostics;

namespace Decav.Gatsb.Application
{
    /// <summary>
    /// Interprets commands from a <see cref="MobileMessage"/>, and executes those commands.
    /// </summary>
    public class CommandInterpreter : IDisposable
    {

        public CommandInterpreter()
        {
            SqlWorkflowPersistenceService persist = new SqlWorkflowPersistenceService(
                GatsbApplicationConfigurationSection.Current.WorkflowSqlConnectionString,
                true, new TimeSpan(5, 0, 0),
                new TimeSpan(0, 0, 15));
            _runtime.AddService(persist);

            ExternalDataExchangeService dataExchSvc = new ExternalDataExchangeService();
            _runtime.AddService(dataExchSvc);
            dataExchSvc.AddService(_gateway);

            _runtime.StartRuntime();
            _runtime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);
        }

        void Runtime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
        {
            if (!(e.Exception is WorkflowTerminatedException))
            {
                if (e.Exception == null)
                {
                    GatsbDiagnostics.WriteLogEntry("An exception occured while a workflow was executing but the exception was null.", EventLogEntryType.Error);
                }
                else
                {
                    GatsbDiagnostics.WriteException("A workflow terminated unexpectedly.", e.Exception);
                }
                

                Debugger.Break();
            }
            else
            {
                Console.WriteLine("Terminated");
            }
        }

        private MessageGateway _gateway = new MessageGateway();
        private WorkflowRuntime _runtime = new WorkflowRuntime();

        private Type WorkflowType
        {
            get
            {
                Type t = Type.GetType("Decav.Gatsb.Application.CommandWorkflow.ExecuteCommandWorkflow,Decav.Gatsb.Application.CommandWorkflow", false);

                if (t == null)
                    throw new ConfigurationErrorsException("The workflow that the CommandInterpreter attempted to run does not exist in the application.");

                return t;
            }
        }

        /// <summary>
        /// Executes workflow for a <see cref="MobileMessage"/>.
        /// </summary>
        /// <param name="message">The message to parse.</param>
        public void Execute(MobileMessage message)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            GatsbServiceApplication.CreateNewThreadApplication();
            if (message.RecipientAddress.ToString().ToLower().StartsWith("wf_"))
            {
                // This is a cookie for a workflow currently executing.  Get that
                // workflow and pass it the message.
               ContinueWorkflow(message);
            }
            else
            {
                // Start a new workflow for the message.
                StartNewWorkflow(message);
            }
        }

        private void ContinueWorkflow(MobileMessage message)
        {
            Guid workflowId = Guid.Empty;
            using (IGatsbDataAdapter adapt = GatsbApplication.Current.GetDataAdapter())
                workflowId = adapt.GetWorkflowForSmsSession(message.RecipientAddress.ToString());

            if (workflowId == Guid.Empty)
                StartNewWorkflow(message); // If the user replied to a workflow that no longer exists, treat it as a new workflow.
            else
                _gateway.RaiseEvent(new WorkflowMessageResponseReceivedEventArgs(workflowId, message));
        }

        private void StartNewWorkflow(MobileMessage message)
        {
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("InputMessage", message);

            WorkflowInstance msgWorkflow = _runtime.CreateWorkflow(WorkflowType, parameters);
            msgWorkflow.Start();
        }

        #region IDisposable Members

        public void Dispose()
        {
            _runtime.StopRuntime();
        }

        #endregion

        [Serializable]
        public class MessageGateway : IWorkflowMessageGateway
        {
            public void RaiseEvent(WorkflowMessageResponseReceivedEventArgs e)
            {
                if (MessageReceived != null)
                    MessageReceived(null, e);
            }

            #region IWorkflowMessageGateway Members

            public event EventHandler<WorkflowMessageResponseReceivedEventArgs> MessageReceived;

            #endregion
        }
    }
}
